package cc.z8g.browser.core.tab

import android.annotation.SuppressLint
import android.app.Activity
import android.graphics.Color
import android.os.Build.VERSION
import android.os.Build.VERSION_CODES
import android.view.View
import android.webkit.CookieManager
import android.webkit.WebSettings
import android.webkit.WebView
import cc.z8g.browser.core.view.CompositeTouchListener
import cc.z8g.browser.log.Logger
import cc.z8g.browser.utils.sp.UserSp
import cc.z8g.browser.utils.MiscUtils

/**
 * Constructs [WebView] instances configured for the browser based on user's preferences and create
 * the headers we will send with requests.
 */
object WebViewFactory {
    private const val TAG = "WebViewFactory"

    /**
     * X-Requested-With 是一个非标准的请求头，通常用于指示请求是通过Ajax发起的。
     * 当浏览器使用 JavaScript 发起 XMLHttpRequest 请求时，通常会自动在请求头中加入这个字段，以指示服务端该请求是异步请求，而不是普通的页面加载请求。
     * 一些 JavaScript 框架和库（比如 jQuery）在发起 Ajax 请求时会自动设置这个字段。
     * 服务器可以根据这个字段来判断请求是普通页面请求还是 Ajax 请求，以便作出相应的处理。
     */
    const val HEADER_REQUESTED_WITH = "X-Requested-With"

    /**
     * X-Wap-Profile 是一个用于传递设备（通常是移动设备）特定信息的请求头字段，通常在早期的移动网页开发中用于识别和优化移动设备的用户体验。
     * 该字段通常包含一个 URL，指向一个描述设备能力和特性的配置文件（通常是 WAP Push Profile），以帮助服务器端根据设备的能力动态调整内容。
     * 随着移动设备和浏览器的发展，使用 X-Wap-Profile 这样的字段的情况已经逐渐减少，因为现代的设备通常能够更好地处理常规的网页内容而无需特定的配置文件。
     */
    const val HEADER_WAP_PROFILE = "X-Wap-Profile"

    /** "Do Not Track"，是一种Web浏览器机制，旨在允许用户通过向网站发送特定的HTTP请求标头来表明他们不希望被跟踪。这个标头提示网站不要对用户的浏览行为进行跟踪或个性化广告投放。 */
    private const val HEADER_DNT = "DNT"

    /** "Save-Data"是一个HTTP请求头标记，用于指示网络请求应该优化数据传输以节省用户的数据使用量。当浏览器或应用程序发送一个请求时，包含了这个标记，服务器可以根据这个标记来返回经过优化的内容，以减少数据传输量。*/
    private const val HEADER_SAVEDATA = "Save-Data"

    /** 通知网站各种隐私和数据偏好的请求头。 */
    fun createRequestHeaders(): Map<String, String> {
        val requestHeaders = mutableMapOf<String, String>()
        if (UserSp.getBoolean(UserSp.KEY_DO_NOT_TRACK)) { // 当用户启用“不追踪”选项时，浏览器会发送一个特殊的HTTP头信息给网站，表明用户不希望被追踪其浏览行为。然而，这并不是所有网站都会遵守的。
            requestHeaders[HEADER_DNT] = "1"
        } else {
            requestHeaders.remove(HEADER_DNT)
        }

        if (UserSp.getBoolean(UserSp.KEY_SAVE_DATA)) { // 当用户启用“节省数据”选项时，浏览器会尝试通过压缩和优化数据传输来减少数据使用量，从而帮助用户降低数据消耗和加快页面加载速度。
            requestHeaders[HEADER_SAVEDATA] = "on"
        } else {
            requestHeaders.remove(HEADER_SAVEDATA)
        }

        if (UserSp.getBoolean(UserSp.KEY_REMOVE_IDENTIFYING_HEADERS)) {
            requestHeaders[HEADER_REQUESTED_WITH] = ""
            requestHeaders[HEADER_WAP_PROFILE] = ""
        } else {
            requestHeaders.remove(HEADER_REQUESTED_WITH)
            requestHeaders.remove(HEADER_WAP_PROFILE)
        }

        return requestHeaders
    }

    /** 基于用户配置创建一个 WebView 实例 */
    fun createWebView(activity: Activity, incognitoMode: Boolean): WebView = WebView(activity).apply {
        id = View.generateViewId()
        tag = CompositeTouchListener().also(::setOnTouchListener)
        isFocusableInTouchMode = true // 将视图设置为在触摸模式下可以获取焦点
        isFocusable = true // 将视图设置为可获取焦点
        if (VERSION.SDK_INT < VERSION_CODES.M) {
            isAnimationCacheEnabled = false // 动画缓存，可以提高性能，但会增加内存
            isAlwaysDrawnWithCacheEnabled = false
        }
        setBackgroundColor(Color.WHITE)

        if (VERSION.SDK_INT >= VERSION_CODES.O) {
            importantForAutofill = View.IMPORTANT_FOR_AUTOFILL_YES // 设备的 Android 版本为 Oreo 或更高版本时，设置一个视图的自动填充属性，以便系统可以更好地处理自动填充操作
        }

        isScrollbarFadingEnabled = true
        isSaveEnabled = true // setSaveEnabled 方法用于控制一个 View 是否保存其状态。当一个视图启用保存功能时，它会保存和恢复其状态（比如焦点状态、滚动位置等）以便在屏幕旋转、重新创建等情况下保持状态的连续性
        overScrollMode = View.OVER_SCROLL_NEVER // 禁止 WebView 在内容边界处过度滚动
        setNetworkAvailable(true) // 当设置为不可用时，WebView 可能会调整其行为以适应无网络连接的情况

        settings.apply {
            mediaPlaybackRequiresUserGesture = true // 用于设置 WebView 是否需要用户手势来播放媒体内容

            mixedContentMode = if (!incognitoMode) {
                WebSettings.MIXED_CONTENT_COMPATIBILITY_MODE // WebView 将允许加载混合内容，但会自动尝试通过特殊的兼容性模式来修复混合内容问题，以确保页面的正常显示。
            } else {
                WebSettings.MIXED_CONTENT_NEVER_ALLOW // WebView 不允许加载任何混合内容，即使这可能导致页面显示问题。
            }

            if (!incognitoMode || MiscUtils.FULL_INCOGNITO) {
                domStorageEnabled = true
                databaseEnabled = true
                cacheMode = WebSettings.LOAD_DEFAULT // 根据缓存机制来加载网页内容，即根据系统的默认缓存策略来确定是否从缓存中加载网页还是直接从网络下载
            } else {
                domStorageEnabled = false
                databaseEnabled = false
                cacheMode = WebSettings.LOAD_NO_CACHE
            }

            setSupportZoom(true) // 是否支持缩放
            builtInZoomControls = true // 用于控制 WebView 是否显示内置的缩放控件
            displayZoomControls = false // 这个属性用于控制 WebView 是否显示缩放控件（放大和缩小按钮）。在这里，false 意味着禁止显示缩放控件
            allowContentAccess = true // 这个属性设置了 WebView 是否允许访问Content
            allowFileAccess = true // 这个属性设置了 WebView 是否允许访问文件
            allowFileAccessFromFileURLs = false // 用于设置 WebView 是否允许通过 file URL 访问本地文件
            allowUniversalAccessFromFileURLs = false // 这个属性设置了 WebView 是否允许从 file URL 访问其他源的内容

            setGeolocationDatabasePath(activity.getDir("geolocation", 0).path) // 设置 WebView 的地理位置数据库路径的方法
        }

        updateBySP(this, activity, incognitoMode)
    }

    @SuppressLint("SetJavaScriptEnabled")
    private fun updateBySP(webView: WebView, activity: Activity, isIncognito: Boolean) {
        val modifiesHeaders = UserSp.getBoolean(UserSp.KEY_DO_NOT_TRACK)
                || UserSp.getBoolean(UserSp.KEY_SAVE_DATA)
                || UserSp.getBoolean(UserSp.KEY_REMOVE_IDENTIFYING_HEADERS)

        val settings = webView.settings
        settings.defaultTextEncodingName = UserSp.getString(UserSp.TEXT_ENCODING)

        if (isIncognito) { // 地理位置信息
            settings.setGeolocationEnabled(false) // 地理位置信息
        } else {
            settings.setGeolocationEnabled(UserSp.getBoolean(UserSp.KEY_LOCATION))
        }

        settings.userAgentString = UserSp.userAgent(activity.application)

        if (UserSp.getBoolean(UserSp.KEY_JAVASCRIPT)) {
            settings.javaScriptEnabled = true
            settings.javaScriptCanOpenWindowsAutomatically = true
        } else {
            settings.javaScriptEnabled = false
            settings.javaScriptCanOpenWindowsAutomatically = false
        }

        if (UserSp.getBoolean(UserSp.KEY_TEXT_REFLOW)) {
            try {
                settings.layoutAlgorithm = WebSettings.LayoutAlgorithm.TEXT_AUTOSIZING
            } catch (e: Exception) {
                // 有一些 KitKat 版本的设备在尝试设置 WebView 的文本自适应模式时会导致崩溃。这表明在某些特定情况下，设置文本自适应模式可能会导致应用程序在 KitKat 版本的设备上出现问题。
                Logger.d(TAG, "Problem setting LayoutAlgorithm to TEXT_AUTOSIZING")
                settings.layoutAlgorithm = WebSettings.LayoutAlgorithm.NORMAL
            }
        } else {
            settings.layoutAlgorithm = WebSettings.LayoutAlgorithm.NORMAL
        }

        settings.blockNetworkImage = UserSp.getBoolean(UserSp.KEY_BLOCK_IMAGES)
        // Modifying headers causes SEGFAULTS, so disallow multi window if headers are enabled.
        settings.setSupportMultipleWindows(UserSp.getBoolean(UserSp.KEY_POPUPS) && !modifiesHeaders)
        settings.useWideViewPort = UserSp.getBoolean(UserSp.KEY_USE_WIDE_VIEWPORT) // 建议为 true
        settings.loadWithOverviewMode = UserSp.getBoolean(UserSp.KEY_OVERVIEW_MODE) // 概览模式会将内容缩小以适应屏幕的宽度
        settings.textZoom = UserSp.getInt(UserSp.KEY_TEXT_ZOOM) + 100

        CookieManager.getInstance().setAcceptThirdPartyCookies(webView, !UserSp.getBoolean(UserSp.KEY_THIRD_PARTY_COOKIES))
    }
}
